/**
* @Author: Chenyongfeng
* @email: kunbei.chen@tuya.com
* @LastEditors: Chenyongfeng
* @file name: app_battery.c
* @Description: 
* @Copyright: HANGZHOU TUYA INFORMATION TECHNOLOGY CO.,LTD
* @Company: http://www.tuya.com
* @Date: 2022-02-22 09:38:57
* @LastEditTime: 2022-02-22 09:38:57
*/
#include "tal_adc.h"
#include "tal_sw_timer.h"
#include "tal_memory.h"
#include "tal_heartbeat.h"

#include "app_battery.h"

#ifndef GET_ARRAY_LEN
#define GET_ARRAY_LEN(x)    (SIZEOF(x) / SIZEOF(x[0]))
#endif

/**
 * @note CR2032 Battery characteristic
 */
#define BATTERY_DEFAULT_TABLE \
    {2400, 0}, /*minimum voltage, percentage*/ \
    {2600, 10},\
    {2800, 20},\
    {2825, 30},\
    {2850, 40},\
    {2875, 50},\
    {2900, 60},\
    {2925, 70},\
    {2950, 80},\
    {2975, 90},\
    {3300, 100},/*maximum voltage, percentage*/ \

//battery sample event and handler
TIMER_ID etimer_battery_event_id;

typedef struct {
    BOOL_T init_flag;
    BOOL_T first_rpt_real;
    APP_BATTERY_INFO_T* info;
    APP_BATTERY_TABLE_T* cal_table;
    UINT8_T cal_table_len;
} APP_BATTERY_RUN_DATA_T;

STATIC APP_BATTERY_TABLE_T s_battery_table_default[] = {BATTERY_DEFAULT_TABLE};
STATIC APP_BATTERY_RUN_DATA_T s_battery_run_data = { .init_flag = FALSE, 
                                                     .first_rpt_real = TRUE, 
                                                     .info = NULL,
                                                     .cal_table = NULL};


/**
* @description: 
* @param {type} 
* @return: 
*/
UINT8_T app_battery_get_battery_level(VOID_T)
{
    INT32_T adc_raw_val = 0;
    UINT16_T temp_val = 0;
    UINT8_T battert_level = 0;
    
    tal_adc_read_single_channel(TUYA_ADC_NUM_0, 1, &adc_raw_val);
    USER_PR_DEBUG("adc_raw_val[%d], ref_vol[%d]\r\n", adc_raw_val, s_battery_run_data.info->adc.ref_vol);

    temp_val = (UINT16_T)adc_raw_val;

    if (temp_val > 3300) {
        temp_val = 3300;
    }
    for(UINT8_T i = 0; i < s_battery_run_data.cal_table_len - 1; i++) {
        if ((temp_val >= s_battery_run_data.cal_table[i].voltage) && (temp_val <= s_battery_run_data.cal_table[i+1].voltage)) {
            battert_level = s_battery_run_data.cal_table[i].percent + 10*(temp_val - s_battery_run_data.cal_table[i].voltage)/(s_battery_run_data.cal_table[i+1].voltage - s_battery_run_data.cal_table[i].voltage);
            USER_PR_DEBUG("percentage %d\r\n",battert_level);
        }
    }

    return (UINT8_T)(battert_level * 2);
}


/**
 * @description: battery event
 * @param [in] timer_id: timer id
 * @param[in] args: null
 * @return: none
 */
STATIC VOID_T app_battery_event_handler(TIMER_ID timer_id, VOID_T *arg)
{
    UINT8_T bat_level = app_battery_get_battery_level();

    if (s_battery_run_data.first_rpt_real) {
        s_battery_run_data.first_rpt_real = FALSE;

        //sample and report
        app_battery_update_and_report_attribute(0x01, bat_level);
        tal_sw_timer_start(etimer_battery_event_id, s_battery_run_data.info->cap_max_period_time, TAL_TIMER_CYCLE);
        return;
    }

    //sample and check value, check write attribute or not.
    app_battery_update_attribute(0x01, bat_level);
}

/**
* @description: 
* @param {type} 
* @return: 
*/
VOID_T app_battery_config_init(APP_BATTERY_INFO_T* bat_info, APP_BATTERY_TABLE_T* table, UINT8_T table_sums)
{
    if (bat_info == NULL) {
        return;
    }

    s_battery_run_data.info = (APP_BATTERY_INFO_T *)tal_malloc(SIZEOF(APP_BATTERY_INFO_T));
    if (s_battery_run_data.info == NULL) {
        USER_PR_DEBUG("s_battery_run_data.info malloc error!\r\n");
        return;
    }
    tal_system_memset(s_battery_run_data.info, 0, SIZEOF(APP_BATTERY_INFO_T));
    tal_system_memcpy(s_battery_run_data.info, bat_info, SIZEOF(APP_BATTERY_INFO_T));

    if ((table != NULL) && (table_sums != 0)) {
        s_battery_run_data.cal_table = (APP_BATTERY_TABLE_T *)tal_malloc(table_sums * SIZEOF(APP_BATTERY_TABLE_T));
        if (s_battery_run_data.cal_table == NULL) {
            USER_PR_DEBUG("s_battery_run_data.cal_table malloc error!\r\n");
            return;
        }
        tal_system_memset(s_battery_run_data.cal_table, 0, table_sums);
        tal_system_memcpy(s_battery_run_data.cal_table, table, table_sums);
        s_battery_run_data.cal_table_len = table_sums;
    } else {
        s_battery_run_data.cal_table = s_battery_table_default;
        s_battery_run_data.cal_table_len = GET_ARRAY_LEN(s_battery_table_default);
    }

    tal_adc_init(TUYA_ADC_NUM_0, &s_battery_run_data.info->adc); 

    //create the sw timer to sample voltage.
    tal_sw_timer_create(app_battery_event_handler, NULL, &etimer_battery_event_id);

    //heartbeat config
    tal_heartbeat_type_set(HEARTBEAT_BATTERY_VOLTAGE);
    tal_heartbeat_period_set(3 * 60 * 1000);
    tal_heartbeat_start();

    s_battery_run_data.init_flag = TRUE;
}

/**
* @description: call when the device new-join or rejoin.
* @param {type} 
* @return: 
*/
VOID_T app_battery_start(VOID_T)
{
    if (s_battery_run_data.init_flag != TRUE) {
        return;
    }

    s_battery_run_data.first_rpt_real = TRUE;
    tal_sw_timer_stop(etimer_battery_event_id);
    tal_sw_timer_start(etimer_battery_event_id, s_battery_run_data.info->cap_first_delay_time, TAL_TIMER_ONCE);
}

/**
* @description: 
* @param {type} 
* @return: 
*/
VOID_T app_battery_stop(VOID_T)
{
    if (s_battery_run_data.init_flag != TRUE) {
        return;
    }

    tal_sw_timer_stop(etimer_battery_event_id);
    s_battery_run_data.first_rpt_real = TRUE;
}

/**
* @description: 
* @param {type} 
* @return: 
*/
VOID_T app_battery_update_attribute(UINT8_T ep, UINT8_T attr_val)
{
    write_sensor_attribute(ep, CLUSTER_POWER_CONFIG_CLUSTER_ID, ATTR_BATTERY_PERCENTAGE_REMAINING_ATTRIBUTE_ID, &attr_val, ATTR_INT8U_ATTRIBUTE_TYPE);
}

/**
* @description: 
* @param {type} 
* @return: 
*/
VOID_T app_battery_update_and_report_attribute(UINT8_T ep, UINT8_T attr_val)
{
    write_sensor_attribute(ep, CLUSTER_POWER_CONFIG_CLUSTER_ID, ATTR_BATTERY_PERCENTAGE_REMAINING_ATTRIBUTE_ID, &attr_val, ATTR_INT8U_ATTRIBUTE_TYPE);
    report_sensor_attribute(ep, CLUSTER_POWER_CONFIG_CLUSTER_ID, ATTR_BATTERY_PERCENTAGE_REMAINING_ATTRIBUTE_ID, ATTR_INT8U_ATTRIBUTE_TYPE, 500);
}

/**
* @description: 
* @param {type} 
* @return: 
*/
VOID_T app_battery_report_attribute(UINT8_T ep)
{
    report_sensor_attribute(ep, CLUSTER_POWER_CONFIG_CLUSTER_ID, ATTR_BATTERY_PERCENTAGE_REMAINING_ATTRIBUTE_ID, ATTR_INT8U_ATTRIBUTE_TYPE, 10);
}

/**
* @description: 
* @param {type} 
* @return: 
*/
VOID_T app_battery_mf_test_start(VOID_T)
{
    s_battery_run_data.first_rpt_real = TRUE;
    tal_sw_timer_stop(etimer_battery_event_id);
    tal_sw_timer_start(etimer_battery_event_id, 10, TAL_TIMER_ONCE);
}


/**
 * @brief heartbeat report callback
 * @note < this is a weak function you can rewrite this API in app layer >
 * 
 * @param[in]   type: heartbeat type
 *
 * @return none
 */
VOID_T tal_heartbeat_report_callback(TAL_HEARTBEAT_TYPE_E type)
{
    app_battery_report_attribute(0x01);
    USER_PR_DEBUG("tal_heartbeat_report_callback\r\n");
}
